package com.hmy.finance.service.statement.statementHandlerFactory;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hmy.ccp.error.exception.HMYRuntimeException;
import com.hmy.finance.api.bo.core.request.enums.FeeTypeEnum;
import com.hmy.finance.common.constants.StatementLossTypeContants;
import com.hmy.finance.common.enums.BusinessBillTypeEnum;
import com.hmy.finance.common.enums.ProfitLossStatusEnum;
import com.hmy.finance.api.bo.core.request.enums.StatementTypeEnum;
import com.hmy.finance.common.utils.FinanceCalculateUtil;
import com.hmy.finance.dal.mapper.*;
import com.hmy.finance.dal.po.*;
import com.hmy.finance.dal.po.json.FeeDetailJson;
import com.hmy.finance.dal.po.json.ProfitLossInfoJson;
import com.hmy.finance.dal.po.json.ReconciliationAdjustJson;
import com.hmy.finance.service.statement.IReconciliationStatementProviderService;
import com.hmy.finance.service.statement.dto.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;


@Slf4j
@Component
public abstract class DefaultStatementHandler {

//    /**
//     * 损益类型枚举
//     */
//    public final String ENTRY_SHIPPING_MAPPING = "ENTRY_SHIPPING";//进场运费
//    public final String EXIT_SHIPPING_MAPPING = "EXIT_SHIPPING";//退场运费
//    public final String ENTRY_MODIFICATION_MAPPING = "ENTRY_MODIFICATION";//改装费
//    public final String CLAIMS_MAPPING = "CLAIMS";//索赔单
//    public final String PAUSE_MAPPING = "PAUSE";//报停单
//    public final String RECEIVABLE_ADJUSTMENT_MAPPING = "RECEIVABLE_ADJUSTMENT";//应收调整
//
//    public final String INITIAL_DAY_FEE = "INITIAL_DAY_FEE";//入场日非全日费用

    @Resource
    private DeviceRentalRecordMapper deviceRentalRecordMapper;

    @Resource
    private DeviceDailyBillMapper deviceDailyBillMapper;

    @Resource
    private ReceivableInventoryMapper receivableInventoryMapper;

    @Resource
    private ProfitLossRecordMapper profitLossRecordMapper;

    @Resource
    private DeviceProfitLossDetailMapper deviceProfitLossDetailMapper;

    @Resource
    private ReconciliationDeviceStatementMapper reconciliationDeviceStatementMapper;


    @Resource
    private IReconciliationStatementProviderService iReconciliationStatementProviderService;

    /**
     * 账单生成执行器
     */
    public abstract ReconciliationStatementDto doStatementHandler(CreateStatementDto createStatementDto);

    /**
     * 计算账单开始时间和结束时间
     *
     * @param statementDevice s
     */
    public abstract void calculateStatementCycle(CalculateStatementDeviceDto statementDevice);

    public abstract void calculateStatementDeviceCycle(CreateStatementDeviceDto statementDevice);

    /**
     * 账单生成执行器类型
     *
     * @return
     */
    public abstract StatementTypeEnum getStatementEnumType();


    /**
     * 默认的账单生成策略执行器。
     */
    public ReconciliationStatementDto defaultStatementHandler(CreateStatementDto createStatementDto) {
        List<CreateStatementDeviceDto> deviceDetails = createStatementDto.getDeviceDetails();

        /*step3.根据逻辑计算账单*/
        /*3.0根据合同单号查询合同历史账单数据;*/
        Long contractIds = deviceDetails.get(0).getContractId();//目前改成了只能有一个合同的设备，所以偷懒取第一个
        LambdaQueryWrapper<ReceivableInventoryPo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ReceivableInventoryPo::getContractId, contractIds);
        queryWrapper.eq(ReceivableInventoryPo::getIsDeleted, 0);
        ReceivableInventoryPo receivableInventoryPo = receivableInventoryMapper.selectOne(queryWrapper);
        if (Objects.isNull(receivableInventoryPo)) {
            throw new HMYRuntimeException(506300015);
        }

        //目前改成了只能有一个合同的设备，所以直接设置销售合同id和no
        createStatementDto.setOrderContractId(receivableInventoryPo.getContractId());
        createStatementDto.setOrderContractNo(receivableInventoryPo.getContractNo());

        BigDecimal totalRevenueAmount = receivableInventoryPo.getTotalRevenueAmount();
        BigDecimal reconciliationedTotalAmount = receivableInventoryPo.getHistoryReconciliationedAmount().max(receivableInventoryPo.getTotalReconciliationedAmount());
        createStatementDto.setReconciliationedAmount(reconciliationedTotalAmount);

        /*3.1 根据设备查询日流水数据;*/
        List<DeviceDailyBillPo> deviceDailyBillRos = getDailyBillRecords(deviceDetails);

        /*取入场第一天流水，设置为入场首日租金;*/
        List<DeviceDailyBillPo> deviceEarliestDailyBillRos = deviceDailyBillMapper.selectEarliestRecordsByDeviceRentalRecordIds(deviceDetails.stream().map(CreateStatementDeviceDto::getDeviceRentalRecordId).collect(Collectors.toList()));
        deviceDetails.forEach(deviceDetail -> deviceEarliestDailyBillRos.stream().filter(d -> d.getDeviceRentalRecordId().equals(deviceDetail.getDeviceRentalRecordId())).findFirst().ifPresent(d -> {
            deviceDetail.setInitialDayRentFee(d.getFee());
            deviceDetail.setInitialDay(d.getChargeDays());
        }));

        /*查询之前已签期账单的金额(设备纬度)，作为跨账单但是没有过第一周期的未来账单费用计算扣除*/
        List<DeviceSignedRentalRo> deviceSignedRentalRos = reconciliationDeviceStatementMapper.selectSignedRentRecordsByDeviceRentalRecordIds(deviceDetails.stream().map(CreateStatementDeviceDto::getDeviceRentalRecordId).collect(Collectors.toList()));
        deviceSignedRentalRos.forEach(deviceSignedRentalRo -> {
            deviceDetails.stream().filter(d -> d.getDeviceRentalRecordId().equals(deviceSignedRentalRo.getDeviceRentalRecordId())).findFirst().ifPresent(d -> {
                d.setSignedRentFee(deviceSignedRentalRo.getSignedRentPayable());
            });
        });

        if (CollectionUtil.isEmpty(deviceDailyBillRos)) {
            throw new HMYRuntimeException(506300011);
        }

        //对deviceDailyBillPos按照合同编号号进行分组
        Map<Long, List<DeviceDailyBillPo>> contractDeviceDailyBillMaps = deviceDailyBillRos.stream().collect(Collectors.groupingBy(DeviceDailyBillPo::getContractId));
        log.info("deviceDailyBillMaps:{}", JSON.toJSONString(contractDeviceDailyBillMaps));

        /*3.2 开始计算账单数据(设备纬度&合同纬度);*/
        /*3.2.2对流水数据进行按合同纬度分组*/
        Map<Long, List<ReconciliationDeviceStatementDto>> contractDeviceStatementMaps = new HashMap<>();//合同id纬度的分组设备数据
        contractDeviceDailyBillMaps.forEach((contractId, deviceContractDailyBillPoList) -> {
            List<ReconciliationDeviceStatementDto> deviceStatementDtos = new ArrayList<>();//设备纬度的账单dtos
            /*3.2.2对流水数据进行按合同、租赁记录分组，获得以deviceNo为key的groupBy分组*/
            Map<Long, List<DeviceDailyBillPo>> deviceDailyBillMaps = deviceContractDailyBillPoList.stream().collect(Collectors.groupingBy(DeviceDailyBillPo::getDeviceRentalRecordId));
            /*3.2.3:设备纬度的统计操作，需要包含未来账单*/
            deviceDailyBillMaps.forEach((rentalRecordId, deviceDailyBillList) -> {
                CreateStatementDeviceDto calculateStatementDeviceDto = deviceDetails.stream().filter(d -> d.getDeviceRentalRecordId().equals(rentalRecordId)).findFirst().get();
                DeviceStatementDetailDto deviceStatementDetailDto = new DeviceStatementDetailDto();
                /*计算账单总天数*/
                deviceStatementDetailDto.setStatementTotalDays(iReconciliationStatementProviderService.calculateDaysBetween(calculateStatementDeviceDto.getCurrentRentalStartTime(), calculateStatementDeviceDto.getCurrentRentalEndTime()) + 1);
                /*用于计算是否报停天数，涉及到是否跨周期的计算，即：*/
                Integer statementPeriodPauseDays = deviceDailyBillList.stream().filter(d -> d.getFee().equals(new BigDecimal(0)) && Boolean.TRUE.equals(d.getPauseFlag())).collect(Collectors.toList()).size();//计算本期账单内报停的天数:并且为全天报停，不是全天报停的不算在内
                /*用于计算计租天数*/
                BigDecimal chargePauseDays = deviceDailyBillList.stream().filter(d -> Boolean.TRUE.equals(d.getPauseFlag())).map(d -> BigDecimal.valueOf(1 - d.getChargeDays())).reduce(BigDecimal.ZERO, BigDecimal::add);//计算本期账单内报停的天数:只要是报停，就计算，不管报停0.5还是0.1天
                deviceStatementDetailDto.setStatementPeriodPauseDays(statementPeriodPauseDays);//账单报停天数

                /*查询是否存在其他损益(进退场运费、改装费)*/
                List<DeviceProfitLossDetailPo> deviceProfitLossDetailRos;
                if (StatementTypeEnum.TOTAL.getValue().equals(createStatementDto.getStatementType())) {
                    //总账单，查询损益的状态需要包含已生效和已对账
                    deviceProfitLossDetailRos = deviceProfitLossDetailMapper.selectDeviceProfitLossDetailByDeviceIdAndCutoffDate(1, calculateStatementDeviceDto.getDeviceId(), calculateStatementDeviceDto.getDeviceRentalRecordId(), calculateStatementDeviceDto.getCurrentRentalEndTime().withHour(23).withMinute(59).withSecond(59));
                } else {
                    //非总账单，查询损益的状态需要包含已生效
                    deviceProfitLossDetailRos = deviceProfitLossDetailMapper.selectDeviceProfitLossDetailByDeviceIdAndCutoffDate(0, calculateStatementDeviceDto.getDeviceId(), calculateStatementDeviceDto.getDeviceRentalRecordId(), calculateStatementDeviceDto.getCurrentRentalEndTime().withHour(23).withMinute(59).withSecond(59));
                }

                /*查询是否存在未生效的报停单进行未来周期费用补足*/
                List<DeviceProfitLossDetailPo> devicePauseLossDetailRos = deviceProfitLossDetailMapper.selectDevicePauseLossDetailByDeviceIdAndCutoffDate(calculateStatementDeviceDto.getDeviceId(), calculateStatementDeviceDto.getDeviceRentalRecordId(), calculateStatementDeviceDto.getCurrentRentalEndTime().withHour(23).withMinute(59).withSecond(59));
                //过滤报停费用
//                List<DeviceProfitLossDetailPo> devicePauseLossDetailRos = deviceProfitLossDetailRos.stream().filter(d -> FeeTypeEnum.PAUSE_FEE.equals(d.getFeeType())).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(devicePauseLossDetailRos)) {
                    //如果账期内存在未处理的报停数据，则将其转换为map
                    //对deviceProfitLossDetailRos中的days进行求和,days为Double型
                    BigDecimal futurePauseDays = new BigDecimal(devicePauseLossDetailRos.stream().map(DeviceProfitLossDetailPo::getDays).reduce(new Double(0), Double::sum));
                    chargePauseDays = chargePauseDays.add(futurePauseDays);
                }
                deviceStatementDetailDto.setChargePauseDays(chargePauseDays);//计租报停天数

                double startDay = 1;//正常入场算1天
                double endDay = 1;//正常退场&不退正常计费算1天
                if (calculateStatementDeviceDto.getCurrentRentalStartTime().toLocalDate().isEqual(calculateStatementDeviceDto.getDeviceBeginRentTime().toLocalDate())) {
                    startDay = FinanceCalculateUtil.getRentalStartDay(calculateStatementDeviceDto.getDeviceBeginRentTime());
                }
                if (Objects.nonNull(calculateStatementDeviceDto.getDeviceEndRentTime()) && calculateStatementDeviceDto.getCurrentRentalEndTime().toLocalDate().isEqual(calculateStatementDeviceDto.getDeviceEndRentTime().toLocalDate())) {
                    endDay = FinanceCalculateUtil.getRentalTerminationDay(calculateStatementDeviceDto.getCurrentRentalEndTime());
                }
                Double rentalDays;
                Double totalDays;

                //总天数-头尾2天+首天入场计租天数+账单结束时间(退场时间)计租天数-报停天数
                rentalDays = new BigDecimal(deviceStatementDetailDto.getStatementTotalDays()).subtract(new BigDecimal(2)).add(new BigDecimal(startDay)).add(new BigDecimal(endDay)).subtract(deviceStatementDetailDto.getChargePauseDays()).doubleValue();
                if (rentalDays.compareTo(new Double(1)) < 0) {
                    //如果计租天数小于1天，则按1天计算
                    rentalDays = 1d;
                }
//                if (Objects.nonNull(calculateStatementDeviceDto.getDeviceEndRentTime())
//                        && calculateStatementDeviceDto.getDeviceBeginRentTime().toLocalDate().isEqual(calculateStatementDeviceDto.getDeviceEndRentTime().toLocalDate())) {
//                    //总天数-头尾2天+首天入场计租天数+当天1天-报停天数
//                    rentalDays = new BigDecimal(deviceStatementDetailDto.getStatementTotalDays()).subtract(deviceStatementDetailDto.getChargePauseDays()).doubleValue();
////                    totalDays = deviceStatementDetailDto.getStatementTotalDays().doubleValue();
//                } else {
//                    //总天数-头尾2天+首天入场计租天数+账单结束时间(退场时间)计租天数-报停天数
//                    rentalDays = new BigDecimal(deviceStatementDetailDto.getStatementTotalDays()).subtract(new BigDecimal(2)).add(new BigDecimal(startDay)).add(new BigDecimal(endDay)).subtract(deviceStatementDetailDto.getChargePauseDays()).doubleValue();
//                    ////总天数-头尾2天+首天入场计租天数+退场时间计租天数
////                    totalDays = new BigDecimal(deviceStatementDetailDto.getStatementTotalDays()).subtract(new BigDecimal(2)).add(new BigDecimal(startDay)).add(new BigDecimal(endDay)).doubleValue();
//                }
                totalDays = deviceStatementDetailDto.getStatementTotalDays().doubleValue();
                deviceStatementDetailDto.setTotalDays(totalDays);
                deviceStatementDetailDto.setRentalDays(rentalDays);

                /*计算各类损益数据*/
                BigDecimal dailyEntryFee = iReconciliationStatementProviderService.getProfitLossSubtotalWithDeviceLoss(deviceProfitLossDetailRos, StatementLossTypeContants.ENTRY_SHIPPING_MAPPING);
                deviceStatementDetailDto.setEntryFee(deviceStatementDetailDto.getEntryFee().add(dailyEntryFee));//进场费

                BigDecimal dailyExitFee = iReconciliationStatementProviderService.getProfitLossSubtotalWithDeviceLoss(deviceProfitLossDetailRos, StatementLossTypeContants.EXIT_SHIPPING_MAPPING);
                deviceStatementDetailDto.setExitFee(deviceStatementDetailDto.getExitFee().add(dailyExitFee));//退场费

                BigDecimal dailyModificationPayable = iReconciliationStatementProviderService.getProfitLossSubtotalWithDeviceLoss(deviceProfitLossDetailRos, StatementLossTypeContants.ENTRY_MODIFICATION_MAPPING);
                deviceStatementDetailDto.setModificationPayable(deviceStatementDetailDto.getModificationPayable().add(dailyModificationPayable));//改装费

                for (DeviceDailyBillPo deviceDailyBill : deviceDailyBillList) {
                    /*3.2.4 计算索赔损益数据*/
                    BigDecimal dailyClaimPayable = iReconciliationStatementProviderService.getProfitLossSubtotal(deviceDailyBill.getProfitLossInfo(), StatementLossTypeContants.CLAIMS_MAPPING);
                    deviceStatementDetailDto.setClaimPayable(deviceStatementDetailDto.getClaimPayable().add(dailyClaimPayable));//索赔费

                    BigDecimal dailyPausePayable = iReconciliationStatementProviderService.getProfitLossSubtotal(deviceDailyBill.getProfitLossInfo(), StatementLossTypeContants.PAUSE_MAPPING);
                    deviceStatementDetailDto.setPausePayable(deviceStatementDetailDto.getPausePayable().add(dailyPausePayable));//报停费

                    BigDecimal initialDayRentFee = iReconciliationStatementProviderService.getProfitLossSubtotal(deviceDailyBill.getProfitLossInfo(), StatementLossTypeContants.INITIAL_DAY_FEE);

                    /*3.2.5 计算租金数据*/
                    BigDecimal dayRentPayable = deviceDailyBill.getFee();
                    deviceStatementDetailDto.setRentPayable(deviceStatementDetailDto.getRentPayable().add(dayRentPayable).add(initialDayRentFee));//租金总计+首日金额损益
                    /*3.2.4费用合计计算*/
                    deviceStatementDetailDto.setTotalAmount(deviceStatementDetailDto.getTotalAmount().add(dayRentPayable).add(dailyEntryFee).add(dailyExitFee).add(dailyClaimPayable).add(dailyModificationPayable).add(dailyPausePayable).add(initialDayRentFee));//设备当期总费用,不包含调整，调整不到设备纬度
                }
                /*封装损益详情数据*/
                List<DeviceStatementLossInfoDto> deviceLossInfos = new ArrayList<>();
                List<DeviceProfitLossDetailPo> deviceLossDetailRos = deviceProfitLossDetailRos.stream().filter(d -> !FeeTypeEnum.PAUSE_FEE.equals(d.getFeeType())).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(deviceLossDetailRos)) {
                    deviceLossDetailRos.forEach(l -> {
                        DeviceStatementLossInfoDto deviceStatementLossInfo = new DeviceStatementLossInfoDto();
                        deviceStatementLossInfo.setDeviceLossId(l.getId());
                        deviceStatementLossInfo.setProfitLossRecordId(l.getProfitLossRecordId());
                        deviceStatementLossInfo.setDeviceRentalRecordId(l.getDeviceRentalRecordId());
                        deviceLossInfos.add(deviceStatementLossInfo);
                    });
                }

                deviceStatementDetailDto.setDeviceLossInfos(deviceLossInfos);

                /*如果包含未来账期，需要计算未来账单信息*/
                /*3.2.5需要包含对未来账单的计算*/
                //取得deviceDailyBillList中feeIncurredTime最晚的日期
                LocalDateTime feeIncurredTime = deviceDailyBillList.stream().map(DeviceDailyBillPo::getFeeIncurredTime).max(LocalDateTime::compareTo).get();
                Integer futureDays = iReconciliationStatementProviderService.calculateDaysBetween(feeIncurredTime, calculateStatementDeviceDto.getCurrentRentalEndTime());
                if (futureDays > 0) {
                    /*查询是否存在未生效的报停单进行未来周期费用补足*/
                    Map<LocalDate, Double> pauseLossMap = new HashMap<>();
                    if (CollectionUtil.isNotEmpty(devicePauseLossDetailRos)) {
                        //如果账期内存在未处理的报停数据，则将其转换为map
                        pauseLossMap = devicePauseLossDetailRos.stream().collect(Collectors.toMap(l -> l.getFeeIncurredTime().toLocalDate(), DeviceProfitLossDetailPo::getDays));
                    }
                    BigDecimal futurePayable = iReconciliationStatementProviderService.futureFeeCalculateHandler(calculateStatementDeviceDto, deviceStatementDetailDto.getRentPayable(), deviceStatementDetailDto.getStatementPeriodPauseDays(), feeIncurredTime, calculateStatementDeviceDto.getCurrentRentalEndTime(), pauseLossMap);
                    deviceStatementDetailDto.setFuturePayable(futurePayable);
                    deviceStatementDetailDto.setRentPayable(deviceStatementDetailDto.getRentPayable().add(deviceStatementDetailDto.getFuturePayable()));
                    deviceStatementDetailDto.setTotalAmount(deviceStatementDetailDto.getTotalAmount().add(deviceStatementDetailDto.getFuturePayable()));
                }
                /*3.2.6 对设备纬度的dto进行封装*/
                ReconciliationDeviceStatementDto reconciliationDeviceStatementDto = reconciliationDeviceStatementDtoPackager(deviceDailyBillList, createStatementDto, calculateStatementDeviceDto, deviceStatementDetailDto);
                deviceStatementDtos.add(reconciliationDeviceStatementDto);
                contractDeviceStatementMaps.put(contractId, deviceStatementDtos);
            });
        });

        /*3.2.4 应收调整损益类别分摊-从损益表中获取损益信息,并进行小计*/
        LambdaQueryWrapper<ProfitLossRecordPo> profitLossSelectWrapper = new LambdaQueryWrapper<>();
        if (StatementTypeEnum.TOTAL.getValue().equals(createStatementDto.getStatementType())) {
            profitLossSelectWrapper.in(ProfitLossRecordPo::getStatus, Arrays.asList(ProfitLossStatusEnum.ALREADY_EFFECTIVE, ProfitLossStatusEnum.RECONCILED));
        } else {
            profitLossSelectWrapper.eq(ProfitLossRecordPo::getStatus, ProfitLossStatusEnum.ALREADY_EFFECTIVE);
        }
        profitLossSelectWrapper.eq(ProfitLossRecordPo::getBusinessBillType, BusinessBillTypeEnum.RECEIVABLE_ADJUSTMENT);
        profitLossSelectWrapper.in(ProfitLossRecordPo::getContractId, contractDeviceDailyBillMaps.keySet());
        profitLossSelectWrapper.between(ProfitLossRecordPo::getFeeIncurredTime, LocalDateTime.of(1991, 1, 1, 0, 0, 0), createStatementDto.getExpenseCycleEndTime());//这边使用的是所选设备的账期结束时间最晚值。
        List<ContractProFitLossDto> contractProFitLossList = new ArrayList<>();
        List<ProfitLossRecordPo> profitLossRecordRos = profitLossRecordMapper.selectList(profitLossSelectWrapper);
        ReconciliationAdjustJson reconciliationAdjust = getReconciliationAdjustJson(profitLossRecordRos, contractProFitLossList);
        BigDecimal totalAdjust = reconciliationAdjust.getAdjustTotal();
        reconciliationAdjust.setAdjustTotal(totalAdjust);

        /*数据封装*/
        /*3.3.1 定义dto进场数据传输与组装*/
        ReconciliationStatementDto reconciliationStatementDto = this.reconciliationStatementPackager(totalRevenueAmount, contractDeviceStatementMaps, contractProFitLossList, reconciliationAdjust, createStatementDto);
        return reconciliationStatementDto;
    }

    private List<DeviceDailyBillPo> getDailyBillRecords(List<CreateStatementDeviceDto> deviceDetails) {
        List<DeviceDailyBillTimeQueryPo> list = new ArrayList<>();
        deviceDetails.forEach(deviceDetail -> {
            DeviceDailyBillTimeQueryPo queryPo = new DeviceDailyBillTimeQueryPo();
            queryPo.setDeviceRentalRecordId(deviceDetail.getDeviceRentalRecordId());
            queryPo.setDeviceId(deviceDetail.getDeviceId());
            queryPo.setDeviceNo(deviceDetail.getDeviceNo());
            queryPo.setContractId(deviceDetail.getContractId());
            queryPo.setContractNo(deviceDetail.getContractNo());
            queryPo.setFeeStatus(Boolean.TRUE);//false表示未生效,或者已作废
            queryPo.setBillStartTime(deviceDetail.getCurrentRentalStartTime().withHour(00).withMinute(00).withSecond(00));

            /*如果设备已经退场，且账单需求拉取到结束时间，则使用账期结束时间来代替账单结束时间，以此拉到退场后一些损益的流水数据*/
            if (Objects.nonNull(deviceDetail.getDeviceEndRentTime()) && deviceDetail.getCurrentRentalEndTime().toLocalDate().isEqual(deviceDetail.getDeviceEndRentTime().toLocalDate())) {
                /*此处加一道栅栏，如果数据库没有账单结束时间，则使用退租时间*/
                if (Objects.nonNull(deviceDetail.getExpenseCycleEndTime())) {
                    queryPo.setBillEndTime(deviceDetail.getExpenseCycleEndTime().withHour(23).withMinute(59).withSecond(59));
                } else {
                    queryPo.setBillEndTime(deviceDetail.getDeviceEndRentTime().withHour(23).withMinute(59).withSecond(59));
                }
            } else {
                queryPo.setBillEndTime(deviceDetail.getCurrentRentalEndTime().withHour(23).withMinute(59).withSecond(59));
            }
            list.add(queryPo);
        });
        return deviceDailyBillMapper.selectDeviceBillsByDeviceNoAndContractNo(list);
    }


    /**
     * 将入参的设备纬度dto转换成账单的设备纬度dto
     */
    private ReconciliationDeviceStatementDto reconciliationDeviceStatementDtoPackager(List<DeviceDailyBillPo> deviceDailyBillList, CreateStatementDto createStatementDto, CreateStatementDeviceDto calculateStatementDeviceDto, DeviceStatementDetailDto deviceStatementDetailDto) {
        ReconciliationDeviceStatementDto reconciliationDeviceStatementDto = new ReconciliationDeviceStatementDto();//设备纬度的统计操作
        reconciliationDeviceStatementDto.setCustomerId(deviceDailyBillList.get(0).getCustomerId());
        reconciliationDeviceStatementDto.setSignatoryId(createStatementDto.getSignatoryId());
        reconciliationDeviceStatementDto.setDeviceRentalRecordId(calculateStatementDeviceDto.getDeviceRentalRecordId());
        reconciliationDeviceStatementDto.setContractId(calculateStatementDeviceDto.getContractId());
        reconciliationDeviceStatementDto.setContractNo(calculateStatementDeviceDto.getContractNo());
        reconciliationDeviceStatementDto.setDeviceModel(calculateStatementDeviceDto.getDeviceModel());
        reconciliationDeviceStatementDto.setDeviceId(calculateStatementDeviceDto.getDeviceId());
        reconciliationDeviceStatementDto.setDeviceNo(calculateStatementDeviceDto.getDeviceNo());
        reconciliationDeviceStatementDto.setDeviceStatementStatusDesc(calculateStatementDeviceDto.getDeviceStatementStatusDesc());
        reconciliationDeviceStatementDto.setRentalStartTime(calculateStatementDeviceDto.getCurrentRentalStartTime());
        reconciliationDeviceStatementDto.setRentalEndTime(calculateStatementDeviceDto.getCurrentRentalEndTime());
        reconciliationDeviceStatementDto.setDeviceBeginRentTime(calculateStatementDeviceDto.getDeviceBeginRentTime());
        reconciliationDeviceStatementDto.setDeviceEndRentTime(calculateStatementDeviceDto.getDeviceEndRentTime());
        reconciliationDeviceStatementDto.setTotalDays(deviceStatementDetailDto.getTotalDays());
        reconciliationDeviceStatementDto.setRentalDays(deviceStatementDetailDto.getRentalDays());
        reconciliationDeviceStatementDto.setRentalUnitPrice(calculateStatementDeviceDto.getRentalUnitPrice());
        reconciliationDeviceStatementDto.setRentalPrice(calculateStatementDeviceDto.getRentalPrice());
        reconciliationDeviceStatementDto.setUseMonthlyRentalPrice(calculateStatementDeviceDto.getUseMonthlyRentalPrice());
        reconciliationDeviceStatementDto.setEntryFee(deviceStatementDetailDto.getEntryFee());
        reconciliationDeviceStatementDto.setExitFee(deviceStatementDetailDto.getExitFee());
        reconciliationDeviceStatementDto.setClaimPayable(deviceStatementDetailDto.getClaimPayable());
        reconciliationDeviceStatementDto.setModificationPayable(deviceStatementDetailDto.getModificationPayable());
        reconciliationDeviceStatementDto.setPausePayable(deviceStatementDetailDto.getPausePayable());
        reconciliationDeviceStatementDto.setTotalAmount(deviceStatementDetailDto.getTotalAmount());
        reconciliationDeviceStatementDto.setRentPayable(deviceStatementDetailDto.getRentPayable());
        reconciliationDeviceStatementDto.setDeviceLossInfos(deviceStatementDetailDto.getDeviceLossInfos());
        reconciliationDeviceStatementDto.setContractExtension(calculateStatementDeviceDto.getContractExtensionInfo());//合同扩展信息
        reconciliationDeviceStatementDto.setCustomerExtension(calculateStatementDeviceDto.getCustomerExtensionInfo());//客户扩展信息
        return reconciliationDeviceStatementDto;
    }

    private ReconciliationStatementDto reconciliationStatementPackager(BigDecimal totalRevenueAmount, Map<Long, List<ReconciliationDeviceStatementDto>> allDeviceStatementMapDtos, List<ContractProFitLossDto> contractProFitLossList, ReconciliationAdjustJson reconciliationAdjust, CreateStatementDto createStatementDto) {
        List<ReconciliationDeviceStatementDto> reconciliationDeviceStatementDtos = new ArrayList<>();
        ReconciliationStatementDto reconciliationStatementDto = new ReconciliationStatementDto();
        //将contractProFitLossList以contractId进行分组
        if (CollectionUtil.isNotEmpty(contractProFitLossList)) {
            reconciliationStatementDto.setContractProFitLosses(contractProFitLossList);//设置合同纬度的调整损益数据
        }

        /*计算各类小计*/
        FeeDetailJson reconciliationSubtotal = new FeeDetailJson();//本期费用小计(设备纬度)
        for (Map.Entry<Long, List<ReconciliationDeviceStatementDto>> entry : allDeviceStatementMapDtos.entrySet()) {
            List<ReconciliationDeviceStatementDto> deviceStatementDtos = entry.getValue();
            reconciliationDeviceStatementDtos.addAll(entry.getValue());

            /*各类损益费用及租金小计，不含调整类(因为调整类的损益不到设备纬度)*/
            deviceStatementDtos.stream().map(ReconciliationDeviceStatementDto::getExitFee).reduce(BigDecimal.ZERO, BigDecimal::add);
            reconciliationSubtotal.setRentalFee(reconciliationSubtotal.getRentalFee().add(deviceStatementDtos.stream().map(ReconciliationDeviceStatementDto::getRentPayable).reduce(BigDecimal.ZERO, BigDecimal::add)));//费用-租金小计
            reconciliationSubtotal.setModificationFee(reconciliationSubtotal.getModificationFee().add(deviceStatementDtos.stream().map(ReconciliationDeviceStatementDto::getModificationPayable).reduce(BigDecimal.ZERO, BigDecimal::add)));//费用-改装费用小计
            reconciliationSubtotal.setEntryShippingFee(reconciliationSubtotal.getEntryShippingFee().add(deviceStatementDtos.stream().map(ReconciliationDeviceStatementDto::getEntryFee).reduce(BigDecimal.ZERO, BigDecimal::add)));//费用-进场运费小计
            reconciliationSubtotal.setExitShippingFee(reconciliationSubtotal.getExitShippingFee().add(deviceStatementDtos.stream().map(ReconciliationDeviceStatementDto::getExitFee).reduce(BigDecimal.ZERO, BigDecimal::add)));//费用-退场运费小计
            reconciliationSubtotal.setClaimFee(reconciliationSubtotal.getClaimFee().add(deviceStatementDtos.stream().map(ReconciliationDeviceStatementDto::getClaimPayable).reduce(BigDecimal.ZERO, BigDecimal::add)));//费用-索赔费用小计
            reconciliationSubtotal.setPauseFee(reconciliationSubtotal.getPauseFee().add(deviceStatementDtos.stream().map(ReconciliationDeviceStatementDto::getPausePayable).reduce(BigDecimal.ZERO, BigDecimal::add)));//费用-报停费用小计
            reconciliationSubtotal.setAdjustTotal(reconciliationSubtotal.getAdjustTotal());//此处表达为账单的损益总计

            /*展示类处理*/
            BigDecimal displayEntryShippingFee = reconciliationSubtotal.getDisplayEntryShippingFee().add(deviceStatementDtos.stream().filter(d -> createStatementDto.getDisplayEntryShippingFee2Excel() || Objects.nonNull(d.getDeviceEndRentTime())).map(ReconciliationDeviceStatementDto::getEntryFee).reduce(BigDecimal.ZERO, BigDecimal::add));
            reconciliationSubtotal.setDisplayEntryShippingFee(displayEntryShippingFee);//费用-进场运费小计

            BigDecimal displayModificationFee = reconciliationSubtotal.getDisplayModificationFee().add(deviceStatementDtos.stream().filter(d -> createStatementDto.getDisplayModificationFee2Excel() || Objects.nonNull(d.getDeviceEndRentTime())).map(ReconciliationDeviceStatementDto::getModificationPayable).reduce(BigDecimal.ZERO, BigDecimal::add));
            reconciliationSubtotal.setDisplayModificationFee(displayModificationFee);//费用-改装费用小计
        }


        reconciliationStatementDto.setCustomerId(reconciliationDeviceStatementDtos.get(0).getCustomerId());
        reconciliationStatementDto.setSignatoryId(reconciliationDeviceStatementDtos.get(0).getSignatoryId());
        reconciliationStatementDto.setProjectName(createStatementDto.getProjectName());
        reconciliationStatementDto.setReconciliationStartDate(createStatementDto.getStatementStartTime());

        //重新设置账单结束时间:设置为reconciliationDeviceStatementDtos结束时间最晚的值
        reconciliationStatementDto.setReconciliationEndDate(reconciliationDeviceStatementDtos.stream().map(ReconciliationDeviceStatementDto::getRentalEndTime).max(LocalDateTime::compareTo).get());
//        reconciliationStatementDto.setReconciliationEndDate(createStatementDto.getStatementEndTime());暂时不用这个时间

        reconciliationStatementDto.setReconciliationSubtotal(reconciliationSubtotal);//本期各类损益费用小计(不含调整类)
        reconciliationStatementDto.setReconciliationAdjust(reconciliationAdjust);//本期调整类费用小计
        FeeDetailJson reconciliationAmount = new FeeDetailJson();//本期费用(本期费用小计+本期费用调整小计)
        reconciliationAmount.setRentalFee(reconciliationSubtotal.getRentalFee().add(reconciliationAdjust.getRentPayableAdjustCost()));//合计-租金小计
        reconciliationAmount.setModificationFee(reconciliationSubtotal.getModificationFee().add(reconciliationAdjust.getModificationAdjustCost()));//合计-改装费用小计
        reconciliationAmount.setEntryShippingFee(reconciliationSubtotal.getEntryShippingFee().add(reconciliationAdjust.getEntryFeeAdjustCost()));//合计-进场运费小计
        reconciliationAmount.setExitShippingFee(reconciliationSubtotal.getExitShippingFee().add(reconciliationAdjust.getExitFeeAdjustCost()));//合计-退场运费小计
        reconciliationAmount.setClaimFee(reconciliationSubtotal.getClaimFee().add(reconciliationAdjust.getClaimPayableAdjustCost()));//合计-索赔费用小计
        reconciliationAmount.setPauseFee(reconciliationSubtotal.getPauseFee());//报停目前没有调整，不需要添加调整费用
        reconciliationAmount.setAdjustTotal(reconciliationAdjust.getAdjustTotal());//合计-调整费用小计
        reconciliationStatementDto.setReconciliationAmount(reconciliationAmount);// 本期损益费用小计(本期各类损益费用小计+本期调整费用小计)

        /*展示类处理*/
        reconciliationAmount.setDisplayEntryShippingFee(reconciliationSubtotal.getDisplayEntryShippingFee().add(reconciliationAdjust.getEntryFeeAdjustCost()));//本期费用-excel展示进场运费（小计+调整）
        reconciliationAmount.setDisplayModificationFee(reconciliationSubtotal.getDisplayModificationFee().add(reconciliationAdjust.getModificationAdjustCost()));//本期费用-excel展示改装运费（小计+调整）

        BigDecimal reconciliationTotalAmount = reconciliationAmount.getRentalFee().add(reconciliationAmount.getAdjustTotal());//本期费用合计,设备租金+调整总计
        BigDecimal displayReconciliationTotalAmount = reconciliationAmount.getRentalFee().add(reconciliationAmount.getDisplayAdjustTotal());//本期费用合计,设备租金+调整总计
        reconciliationStatementDto.setReconciliationTotalAmount(reconciliationTotalAmount);//本期费用合计
        reconciliationStatementDto.setDisplayReconciliationTotalAmount(displayReconciliationTotalAmount);//本期展示费用合计

        reconciliationStatementDto.setContractTotalAmount(totalRevenueAmount);//合同总金额
        reconciliationStatementDto.setReconciliationedTotalAmount(createStatementDto.getReconciliationedAmount());//已对账金额
        reconciliationStatementDto.setStatementDate(LocalDateTime.now());//账单日期是创建时间
        reconciliationStatementDto.setStatementType(StatementTypeEnum.getEnumByValue(createStatementDto.getStatementType()));//账单类型
        reconciliationStatementDto.setDisplayEntryShippingFee2Excel(createStatementDto.getDisplayEntryShippingFee2Excel());// Excel中是否展示进场运费
        reconciliationStatementDto.setDisplayModificationFee2Excel(createStatementDto.getDisplayModificationFee2Excel());//Excel中是否展示退场运费
        reconciliationStatementDto.setCustomerExtensionInfo(reconciliationDeviceStatementDtos.get(0).getCustomerExtension());//客户扩展信息
        reconciliationStatementDto.setSaleManagerId(createStatementDto.getSaleManagerId());//销售经理ID
        reconciliationStatementDto.setSaleManagerName(createStatementDto.getSaleManagerName());//销售经理姓名
        reconciliationStatementDto.setDeptId(createStatementDto.getSaleManagerDeptId());//部门ID
        reconciliationStatementDto.setMark(createStatementDto.getMark());//对账单备注
        reconciliationStatementDto.setDeviceRentalStatementDetails(reconciliationDeviceStatementDtos);//设备对账单详情

        return reconciliationStatementDto;

    }


    public boolean isLastReconciliationTimeBetween(LocalDateTime targetDateTime, LocalDateTime startDateTime, LocalDateTime endDateTime) {
        return !targetDateTime.isBefore(startDateTime) && !targetDateTime.isAfter(endDateTime);
    }

//    /**
//     * 计算两个时间之间的天数
//     */
//    public static Integer calculateDaysBetween(LocalDateTime startTime, LocalDateTime endTime) {
//        LocalDate startDate = startTime.toLocalDate();
//        LocalDate endDate = endTime.toLocalDate();
//        return Integer.parseInt(String.valueOf(ChronoUnit.DAYS.between(startDate, endDate)));
//    }

    public List<DeviceRentalRecordPo> getDeviceByDeviceNoAndContractNo(List<CalculateStatementDeviceDto> deviceDetails) {
        List<DeviceRentalRecordPo> deviceRentalRecordParams = BeanUtil.copyToList(deviceDetails, DeviceRentalRecordPo.class);
        List<DeviceRentalRecordPo> deviceRentalRecordPos = deviceRentalRecordMapper.selectDeviceByDeviceNoAndContractNo(deviceRentalRecordParams);
        return deviceRentalRecordPos;
    }

//    /**
//     * @param rentPayable          本期租金目前总额
//     * @param feeIncurredTime      未来账期最早时间的一天
//     * @param currentRentalEndTime 当前账单结束时间（予定时间和退场时间比对取前者）
//     * @return 未来账期金额
//     */
//    public BigDecimal futureFeeCalculateHandler(CreateStatementDeviceDto calculateStatementDeviceDto, BigDecimal rentPayable, Integer statementPeriodPauseDays, LocalDateTime feeIncurredTime, LocalDateTime currentRentalEndTime, Map<LocalDate, Double> pauseLossMap) {
//        LocalDateTime deviceBeginRentTime = calculateStatementDeviceDto.getDeviceBeginRentTime();
//        BigDecimal dailyRentalPrice = calculateStatementDeviceDto.getDailyRentalPrice();
//        /**
//         * 首个周期月租金额(一个月的价格，不是一天的价格)
//         */
//        BigDecimal firstCycleMonthlyRentalPrice = calculateStatementDeviceDto.getMonthlyRentalPrice();
//
//        BigDecimal monthlyRentalPrice = calculateStatementDeviceDto.getMonthlyRentalPrice();
//
//        /*1.是否是入场日是否为全租金;*/
//        BigDecimal initialDayRentFee = calculateStatementDeviceDto.getInitialDayRentFee();
//        if (initialDayRentFee.compareTo(BigDecimal.ZERO) > 0 && initialDayRentFee.compareTo(dailyRentalPrice) < 0) {
//            //如果入场首日租金小于日租金，则说明首日入场不是全日计费。需要将首月的费用减去差值作为首月最大值
//            BigDecimal lossRent = calculateStatementDeviceDto.getMonthlyRentalPrice().multiply(new BigDecimal(1 - calculateStatementDeviceDto.getInitialDay())).divide(new BigDecimal(30), 2, RoundingMode.HALF_UP);//四舍五入
//            firstCycleMonthlyRentalPrice = firstCycleMonthlyRentalPrice.subtract(lossRent);
//        }
//
//        /*2.如果是非首次拉取，则需要根已签期账单的金额(设备纬度)是否还是小于月租金(max金额)进行对比，排除掉多个账单拉取中都不满第一周期的情况*/
//        if (calculateStatementDeviceDto.getSignedRentFee().compareTo(BigDecimal.ZERO) > 0 && calculateStatementDeviceDto.getSignedRentFee().compareTo(firstCycleMonthlyRentalPrice) < 0) {
//            firstCycleMonthlyRentalPrice = firstCycleMonthlyRentalPrice.subtract(calculateStatementDeviceDto.getSignedRentFee());
//        }
//
//        BigDecimal plannedRent = rentPayable;
//        BigDecimal futurePayable = BigDecimal.ZERO;//未来预计账单
//        Integer futureDays = calculateDaysBetween(feeIncurredTime, currentRentalEndTime);
//
//        if (futureDays > 0) {
//            //根据latestFeeIncurredTime和currentRentalEndTime返回中间的时间list
//            List<LocalDate> futureTimeList = new ArrayList<>();
//            LocalDate tempTime = feeIncurredTime.toLocalDate().plusDays(1);
//            //设置currentRentalEndTime为23点59分59秒
////            currentRentalEndTime = currentRentalEndTime.withHour(23).withMinute(59).withSecond(59);
//            while (!tempTime.isAfter(currentRentalEndTime.toLocalDate())) {
//                futureTimeList.add(tempTime);
//                tempTime = tempTime.plusDays(1);
//            }
//            //根据报停账单流水中的报停天数向后推迟日转月时间:
//            LocalDate firstCycleFinalDate;
//            if (calculateStatementDeviceDto.getInitialDay().equals(new Double(0))) {
//                //第一天不算费用。当天不算一天
//                firstCycleFinalDate = deviceBeginRentTime.plusDays(30).toLocalDate().plusDays(statementPeriodPauseDays);
//            } else {
//                //第一天有费用，当天算一天
//                firstCycleFinalDate = deviceBeginRentTime.plusDays(30 - 1).toLocalDate().plusDays(statementPeriodPauseDays);
//            }
//
//            //对剩余的报停天数进行处理
//            BigDecimal futurePausePayable = futurePauseFeeCalculateHandler(futureTimeList, pauseLossMap, firstCycleFinalDate, dailyRentalPrice, firstCycleMonthlyRentalPrice, rentPayable);
//            futurePayable = futurePayable.add(futurePausePayable);
//
//            BigDecimal beforeDateFuturePayable = BigDecimal.ZERO;
//            BigDecimal afterDateFuturePayable = BigDecimal.ZERO;
//
//            Map<Boolean, List<LocalDate>> map = futureTimeList.stream().collect(Collectors.partitioningBy(dateTime -> !dateTime.isAfter(firstCycleFinalDate)));
//            for (Map.Entry<Boolean, List<LocalDate>> entry : map.entrySet()) {
//                Boolean isBefore = entry.getKey();
//                if (isBefore) {
//                    /*step2.1如果小于1，未来账单跨周期情况下:说明账单在第一个月，但是会跨到第N个月，则判断累加的租金是否大于月租金，如果大于月租金，则对未来账单进行补。判断日转月*/
//                    List<LocalDate> firstPeriod = entry.getValue();//第一个账单周期
//                    firstPeriod.sort(LocalDate::compareTo);
//                    for (LocalDate dateTime : firstPeriod) {
//                        log.info("dateTime:{}", JSON.toJSON(dateTime));
//                        if (plannedRent.add(dailyRentalPrice).compareTo(firstCycleMonthlyRentalPrice) < 0) {
//                            plannedRent = plannedRent.add(dailyRentalPrice);
//                            beforeDateFuturePayable = beforeDateFuturePayable.add(dailyRentalPrice);
//                        } else {
//                            plannedRent = firstCycleMonthlyRentalPrice;
//                            beforeDateFuturePayable = firstCycleMonthlyRentalPrice.subtract(rentPayable);//减去已经计算的租金
//                            break;
//                        }
//                    }
//                } else {
//                    /*step2.2如果小于1，未来账单不跨周期情况下:说明账单还是在第一个月，则判断累加的租金是否大于月租金，如果大于月租金，则对未来账单进行补。判断日转月*/
//                    afterDateFuturePayable = afterDateFuturePayable.add(monthlyRentalPrice.divide(new BigDecimal(30), 3, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(entry.getValue().size())));
//                }
//            }
//            futurePayable = futurePayable.add(beforeDateFuturePayable).add(afterDateFuturePayable);
//        }
//        return futurePayable;
//    }

//    /**
//     * @param futureTimeList               未来账期时间
//     * @param pauseLossMap                 报停map：key:报停日期;value:报停系数（1为全日报停）
//     * @param firstCycleFinalDate          日转月日期
//     * @param dailyRentalPrice             日租金
//     * @param firstCycleMonthlyRentalPrice 首轮周期月租金（月租金-首日入场差额-已签署账单金额）
//     * @return 未来报停费用
//     */
//    private BigDecimal futurePauseFeeCalculateHandler(List<LocalDate> futureTimeList, Map<LocalDate, Double> pauseLossMap, LocalDate firstCycleFinalDate, BigDecimal dailyRentalPrice, BigDecimal firstCycleMonthlyRentalPrice, BigDecimal rentPayable) {
//        BigDecimal futurePausePayable = BigDecimal.ZERO;
//
//        BigDecimal plannedRent = BigDecimal.ZERO;//rentPayable
//
//        BigDecimal beforeDateFuturePausePayable = BigDecimal.ZERO;
//        BigDecimal afterDateFuturePausePayable = BigDecimal.ZERO;
//        //对futureTimeList进行排序
//        futureTimeList.stream().sorted(Comparator.naturalOrder()).collect(Collectors.toList());
//
//        List<LocalDate> datesToRemove = new ArrayList<>();
//        for (LocalDate futureDate : futureTimeList) {
//            BigDecimal pauseLossRatio = BigDecimal.ONE.subtract(new BigDecimal(pauseLossMap.getOrDefault(futureDate, new Double(0.0))));//实际费用计算比例(1-报停比例)
//            BigDecimal fee = BigDecimal.ZERO;
//            if (!futureDate.isAfter(firstCycleFinalDate)) {
//                //早于或等于日转月时间
//                //pauseLossRatio在大于等于0小于1:说明是部分（0<pauseLossRatio<1）或全日报停（pauseLossRatio=0）报停
//                if (pauseLossRatio.compareTo(new BigDecimal(1)) < 0) {
//                    //此类情况说明只是报停了一部分时间
//                    fee = dailyRentalPrice.multiply(pauseLossRatio);
//                    firstCycleFinalDate = firstCycleFinalDate.plusDays(1);//day2MonthDate日转月时间往后延一天
//                    datesToRemove.add(futureDate);//未来周期里这天已经被部分报停，所以不用再计算未来账单
//                }
//                if (plannedRent.add(fee).compareTo(firstCycleMonthlyRentalPrice) < 0) {
//                    plannedRent = plannedRent.add(fee);
//                    beforeDateFuturePausePayable = beforeDateFuturePausePayable.add(fee);
//                } else {
//                    plannedRent = firstCycleMonthlyRentalPrice;
//                    beforeDateFuturePausePayable = firstCycleMonthlyRentalPrice.subtract(rentPayable);//减去已经计算的租金
//                }
//            } else {
//                if (pauseLossRatio.compareTo(new BigDecimal(1)) < 0) {
//                    //晚于日转月时间
//                    fee = firstCycleMonthlyRentalPrice.multiply(pauseLossRatio.setScale(1, BigDecimal.ROUND_HALF_UP)).divide(new BigDecimal(30), 3, BigDecimal.ROUND_HALF_UP);
//                    datesToRemove.add(futureDate); //未来周期里这天已经被报停，所以不用再计算未来账单
//                    afterDateFuturePausePayable.add(fee);
//                }
//            }
//            log.info("日期:{},报停后计算租金为:{}", futureDate, fee);
//        }
//        futureTimeList.removeAll(datesToRemove);
//        futurePausePayable = futurePausePayable.add(beforeDateFuturePausePayable).add(afterDateFuturePausePayable);
//        log.info("futurePauseFeeCalculateHandler 计算未来报停总费用为:{}", futurePausePayable);
//        return futurePausePayable;
//    }
//

//    /**
//     * 计算各类损益小计，这个函数写的真鸡儿难看，但是没啥好办法
//     *
//     * @param profitLossInfos 损益信息
//     * @param billType        损益类型
//     * @return profitLossSubtotal
//     */
//    public BigDecimal getProfitLossSubtotal(List<ProfitLossInfoJson> profitLossInfos, String billType) {
//        BigDecimal profitLossSubtotal = new BigDecimal(0);
//        if (CollectionUtil.isEmpty(profitLossInfos)) {
//            return profitLossSubtotal;//如果损益单list为空，则直接返回0
//        }
//        for (ProfitLossInfoJson profitLossInfoJson : profitLossInfos) {
////            /*进场运费损益*/
////            if (billType.equals(ENTRY_SHIPPING_MAPPING)) {
////                if (profitLossInfoJson.getBusinessBillType().equals(BusinessBillTypeEnum.ENTRY.getValue()) && profitLossInfoJson.getType().equals(FeeTypeEnum.SHIPPING_FEE.getValue())) {
////                    profitLossSubtotal = profitLossSubtotal.add(profitLossInfoJson.getFee());//如果是运费，则加上运费
////                }
////                if (profitLossInfoJson.getBusinessBillType().equals(BusinessBillTypeEnum.ENTRY.getValue()) && profitLossInfoJson.getType().equals(FeeTypeEnum.SHIPPING_FEE_WAIVER.getValue())) {
////                    profitLossSubtotal = profitLossSubtotal.subtract(profitLossInfoJson.getFee());//如果是运费减免，则减去运费
////                }
////            }
////            /*退场运费损益*/
////            if (billType.equals(EXIT_SHIPPING_MAPPING)) {
////                if (profitLossInfoJson.getBusinessBillType().equals(BusinessBillTypeEnum.EXIT.getValue()) && profitLossInfoJson.getType().equals(FeeTypeEnum.SHIPPING_FEE.getValue())) {
////                    profitLossSubtotal = profitLossSubtotal.add(profitLossInfoJson.getFee());//如果是运费，则加上运费
////                }
////                if (profitLossInfoJson.getBusinessBillType().equals(BusinessBillTypeEnum.EXIT.getValue()) && profitLossInfoJson.getType().equals(FeeTypeEnum.SHIPPING_FEE_WAIVER.getValue())) {
////                    profitLossSubtotal = profitLossSubtotal.subtract(profitLossInfoJson.getFee());//如果是运费减免，则减去运费
////                }
////            }
////            /*进场改装费损益(不管进退场)*/
////            if (billType.equals(ENTRY_MODIFICATION_MAPPING)) {
////                if (profitLossInfoJson.getType().equals(FeeTypeEnum.MODIFICATION_FEE.getValue())) {
////                    profitLossSubtotal = profitLossSubtotal.add(profitLossInfoJson.getFee());//如果是改装费，则加上改装费
////                }
////                if (profitLossInfoJson.getBusinessBillType().equals(BusinessBillTypeEnum.ENTRY.getValue()) && profitLossInfoJson.getType().equals(FeeTypeEnum.MODIFICATION_FEE_WAIVER.getValue())) {
////                    profitLossSubtotal = profitLossSubtotal.subtract(profitLossInfoJson.getFee());//如果是改装费减免，则减去改装费
////                }
////            }
//            /*索赔单损益(不管进退场)*/
//            if (billType.equals(CLAIMS_MAPPING)) {
//                if (profitLossInfoJson.getType().equals(FeeTypeEnum.CLAIMS_FEE.getValue())) {
//                    profitLossSubtotal = profitLossSubtotal.add(profitLossInfoJson.getFee());//如果是索赔费用，则加上索赔费用
//                }
//                if (profitLossInfoJson.getType().equals(FeeTypeEnum.CLAIMS_FEE_WAIVER.getValue())) {
//                    profitLossSubtotal = profitLossSubtotal.subtract(profitLossInfoJson.getFee());//如果是索赔费用减免，则减去索赔费用
//                }
//            }
//            /*报停单损益(不管进退场)*/
//            if (billType.equals(PAUSE_MAPPING)) {
//                if (profitLossInfoJson.getType().equals(FeeTypeEnum.SUPPLEMENTARY_PAUSE_FEE.getValue())) {
//                    profitLossSubtotal = profitLossSubtotal.subtract(profitLossInfoJson.getFee());//补录报停，加上报停费用
//                }
//            }
//
//            /*入场日非全日费用(比如入场那天只算半天费用)*/
//            if (billType.equals(INITIAL_DAY_FEE)) {
//                if (profitLossInfoJson.getType().equals(FeeTypeEnum.INITIAL_DAY_FEE.getValue())) {
//                    profitLossSubtotal = profitLossSubtotal.add(profitLossInfoJson.getFee());
//                }
//                if (profitLossInfoJson.getType().equals(FeeTypeEnum.INITIAL_DAY_FEE_WAIVER.getValue())) {
//                    profitLossSubtotal = profitLossSubtotal.subtract(profitLossInfoJson.getFee());
//                }
//            }
//        }
//        return profitLossSubtotal;
//    }

//    public BigDecimal getProfitLossSubtotalWithDeviceLoss(List<DeviceProfitLossDetailPo> profitLossInfos, String billType) {
//        BigDecimal profitLossSubtotal = new BigDecimal(0);
//        if (CollectionUtil.isEmpty(profitLossInfos)) {
//            return profitLossSubtotal;//如果损益单list为空，则直接返回0
//        }
//
//        for (DeviceProfitLossDetailPo deviceProfitLossDetail : profitLossInfos) {
//            /*进场运费损益*/
//            if (billType.equals(ENTRY_SHIPPING_MAPPING)) {
//                if (deviceProfitLossDetail.getBusinessBillType().equals(BusinessBillTypeEnum.ENTRY) && deviceProfitLossDetail.getFeeType().equals(FeeTypeEnum.SHIPPING_FEE)) {
//                    profitLossSubtotal = profitLossSubtotal.add(deviceProfitLossDetail.getFee());//如果是运费，则加上运费
//                }
//                if (deviceProfitLossDetail.getBusinessBillType().equals(BusinessBillTypeEnum.ENTRY) && deviceProfitLossDetail.getFeeType().equals(FeeTypeEnum.SHIPPING_FEE_WAIVER)) {
//                    profitLossSubtotal = profitLossSubtotal.subtract(deviceProfitLossDetail.getFee());//如果是运费减免，则减去运费
//                }
//            }
//            /*退场运费损益*/
//            if (billType.equals(EXIT_SHIPPING_MAPPING)) {
//                if (deviceProfitLossDetail.getBusinessBillType().equals(BusinessBillTypeEnum.EXIT) && deviceProfitLossDetail.getFeeType().equals(FeeTypeEnum.SHIPPING_FEE)) {
//                    profitLossSubtotal = profitLossSubtotal.add(deviceProfitLossDetail.getFee());//如果是运费，则加上运费
//                }
//                if (deviceProfitLossDetail.getBusinessBillType().equals(BusinessBillTypeEnum.EXIT) && deviceProfitLossDetail.getFeeType().equals(FeeTypeEnum.SHIPPING_FEE_WAIVER)) {
//                    profitLossSubtotal = profitLossSubtotal.subtract(deviceProfitLossDetail.getFee());//如果是运费减免，则减去运费
//                }
//            }
//            /*进场改装费损益(不管进退场)*/
//            if (billType.equals(ENTRY_MODIFICATION_MAPPING)) {
//                if (deviceProfitLossDetail.getFeeType().equals(FeeTypeEnum.MODIFICATION_FEE)) {
//                    profitLossSubtotal = profitLossSubtotal.add(deviceProfitLossDetail.getFee());//如果是改装费，则加上改装费
//                }
//                if (deviceProfitLossDetail.getFeeType().equals(FeeTypeEnum.MODIFICATION_FEE_WAIVER)) {
//                    profitLossSubtotal = profitLossSubtotal.subtract(deviceProfitLossDetail.getFee());//如果是改装费减免，则减去改装费
//                }
//            }
//        }
//        return profitLossSubtotal;
//    }

    public ReconciliationAdjustJson getReconciliationAdjustJson(List<ProfitLossRecordPo> profitLossRecordRos, List<ContractProFitLossDto> contractProFitLossList) {
        if (CollectionUtil.isNotEmpty(profitLossRecordRos)) {
            for (ProfitLossRecordPo profitLossRecordPo : profitLossRecordRos) {
                ContractProFitLossDto contractProFitLossDto = new ContractProFitLossDto();
                contractProFitLossDto.setContractId(profitLossRecordPo.getContractId());
                contractProFitLossDto.setProfitLossId(profitLossRecordPo.getId());
                getAdjustProfitLossSubtotal(profitLossRecordPo, contractProFitLossDto);
                contractProFitLossList.add(contractProFitLossDto);
            }
        }
        /*3.2.4计算调整费用小计*/
        ReconciliationAdjustJson reconciliationAdjust = new ReconciliationAdjustJson();
        reconciliationAdjust.setRentPayableAdjustCost(contractProFitLossList.stream().map(ContractProFitLossDto::getPayableRentAdjust).reduce(BigDecimal.ZERO, BigDecimal::add));//租金调整
        reconciliationAdjust.setEntryFeeAdjustCost(contractProFitLossList.stream().map(ContractProFitLossDto::getEntryFreightAdjust).reduce(BigDecimal.ZERO, BigDecimal::add));//进场费用调整
        reconciliationAdjust.setExitFeeAdjustCost(contractProFitLossList.stream().map(ContractProFitLossDto::getExitFreightAdjust).reduce(BigDecimal.ZERO, BigDecimal::add));//退场费用调整
        reconciliationAdjust.setClaimPayableAdjustCost(contractProFitLossList.stream().map(ContractProFitLossDto::getPayableClaimAdjust).reduce(BigDecimal.ZERO, BigDecimal::add));//索赔费用调整
        reconciliationAdjust.setModificationAdjustCost(contractProFitLossList.stream().map(ContractProFitLossDto::getPayableModificationAdjust).reduce(BigDecimal.ZERO, BigDecimal::add));//改装费用调整
        BigDecimal totalAdjust = reconciliationAdjust.getAdjustTotal();
        reconciliationAdjust.setAdjustTotal(totalAdjust);
        return reconciliationAdjust;
    }

    public void getAdjustProfitLossSubtotal(ProfitLossRecordPo profitLossRecordPo, ContractProFitLossDto contractProFitLossDto) {
        BigDecimal adjustProfitLoss = new BigDecimal(0);
        if (FeeTypeEnum.SHIPPING_FEE.getValue().equals(profitLossRecordPo.getFeeType().getValue())) {
            contractProFitLossDto.setEntryFreightAdjust(adjustProfitLoss.add(profitLossRecordPo.getTotalFeeAmount()));//进场运费调整
        }
        if (FeeTypeEnum.SHIPPING_FEE_WAIVER.getValue().equals(profitLossRecordPo.getFeeType().getValue())) {
            contractProFitLossDto.setEntryFreightAdjust(adjustProfitLoss.subtract(profitLossRecordPo.getTotalFeeAmount()));//如果是运费减免，则减去运费
        }
        if (FeeTypeEnum.MODIFICATION_FEE.getValue().equals(profitLossRecordPo.getFeeType().getValue())) {
            contractProFitLossDto.setPayableModificationAdjust(adjustProfitLoss.add(profitLossRecordPo.getTotalFeeAmount()));///如果是改装费，则加上改装费
        }
        if (FeeTypeEnum.MODIFICATION_FEE_WAIVER.getValue().equals(profitLossRecordPo.getFeeType().getValue())) {
            contractProFitLossDto.setPayableModificationAdjust(adjustProfitLoss.subtract(profitLossRecordPo.getTotalFeeAmount()));//如果是改装费减免，则减去改装费
        }
        if (FeeTypeEnum.CLAIMS_FEE.getValue().equals(profitLossRecordPo.getFeeType().getValue())) {
            contractProFitLossDto.setPayableClaimAdjust(adjustProfitLoss.add(profitLossRecordPo.getTotalFeeAmount()));//如果是索赔费用，则加上索赔费用
        }
        if (FeeTypeEnum.CLAIMS_FEE_WAIVER.getValue().equals(profitLossRecordPo.getFeeType().getValue())) {
            contractProFitLossDto.setPayableClaimAdjust(adjustProfitLoss.subtract(profitLossRecordPo.getTotalFeeAmount()));//如果是索赔费用减免，则减去索赔费用
        }
//        if (FeeTypeEnum.PAUSE_FEE.getValue().equals(profitLossRecordPo.getFeeType().getValue())) {
//            contractProFitLossDto.setPayablePauseAdjust(adjustProfitLoss.add(profitLossRecordPo.getTotalFeeAmount()));//如果是报停费用，则加上报停费用
//        }
//        if (FeeTypeEnum.PAUSE_FEE_WAIVER.getValue().equals(profitLossRecordPo.getFeeType().getValue())) {
//            contractProFitLossDto.setPayablePauseAdjust(adjustProfitLoss.subtract(profitLossRecordPo.getTotalFeeAmount()));//如果是报停费用减免，则减去报停费用
//        }
        if (FeeTypeEnum.RENT_FEE.getValue().equals(profitLossRecordPo.getFeeType().getValue())) {
            contractProFitLossDto.setPayableRentAdjust(adjustProfitLoss.add(profitLossRecordPo.getTotalFeeAmount()));//如果是租金费用，则加上租金调整费用
        }
        if (FeeTypeEnum.RENT_FEE_WAIVER.getValue().equals(profitLossRecordPo.getFeeType().getValue())) {
            contractProFitLossDto.setPayableRentAdjust(adjustProfitLoss.subtract(profitLossRecordPo.getTotalFeeAmount()));//如果是租金减免，则减去租金调整费用
        }
    }

}
